#include <stdio.h>
#include <stdlib.h>

#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <MMSystem.h>
#else
#include <unistd.h>
#include<sys/time.h>
#endif

#include <boost/thread.hpp>
#include "wiiuse.h"

#include "Wiimote.h"
#include "WiimoteManager.h"
#include "TiVoConnection.h"

using namespace MediaCenter;

#define MAX_WIIMOTES				4
#define CHANNEL_SWAP_TIME			500

unsigned int last_channel_change;
bool Running;
WiimoteManager* wmManager;
TiVoConnection* tivoConnection;

#ifndef WIN32
unsigned int timeGetTime()
{
    struct timeval now;
    gettimeofday(&now, NULL);
    return now.tv_usec/1000 + now.tv_sec * 1000;
} 
#endif

/**
 *	@brief Callback that handles an event.
 *
 *	@param wm		Pointer to a wiimote_t structure.
 *
 *	This function is called automatically by the wiiuse library when an
 *	event occurs on the specified wiimote.
 */
void handle_event(Wiimote* wm)
{
	if (wm->ButtonPress(WIIMOTE_BUTTON_ONE))
	{
		#ifndef WIN32
			system("xset dpms force off");
		#else
			SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) 2);
		#endif
	}

	if (wm->ButtonPress(WIIMOTE_BUTTON_TWO))
	{
		#ifndef WIN32
			system("xset dpms force on");
		#else
			SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM) -1);
		#endif
	}
	
	if (wm->AccelerationEnabled())
	{

        if (wm->ButtonDown(WIIMOTE_BUTTON_B) && (timeGetTime() - last_channel_change > CHANNEL_SWAP_TIME))
        {	
			if (wm->GetAcceleration().x < 100)
            {
                printf("TiVo Command: Channel Up\n");
                last_channel_change = timeGetTime();
				tivoConnection->SendCommand("IRCODE CHANNELUP\r\n");
            }
            else if (wm->GetAcceleration().x > 150)
            {
                printf("TiVo Command: Channel Down\n");
                last_channel_change = timeGetTime();
                tivoConnection->SendCommand("IRCODE CHANNELDOWN\r\n");
            }
        }
    }


	if (wm->ButtonPress(WIIMOTE_BUTTON_PLUS))
    {
        printf("TiVo Command: Channel Up\n");
		tivoConnection->SendCommand("IRCODE CHANNELUP\r\n");
    }
    else if (wm->ButtonPress(WIIMOTE_BUTTON_MINUS))
    {
        printf("TiVo Command: Channel Down\n");
        tivoConnection->SendCommand("IRCODE CHANNELDOWN\r\n");
    }

	if (wm->ButtonPress(WIIMOTE_BUTTON_UP))
	{
		printf("TiVo Command: Up\n");
        tivoConnection->SendCommand("IRCODE UP\r\n");
	}
	else if (wm->ButtonPress(WIIMOTE_BUTTON_DOWN))
	{
		printf("TiVo Command: Down\n");
        tivoConnection->SendCommand("IRCODE DOWN\r\n");
	}
	else if (wm->ButtonPress(WIIMOTE_BUTTON_LEFT))
	{
		printf("TiVo Command: Left\n");
        tivoConnection->SendCommand("IRCODE LEFT\r\n");
	}
	else if (wm->ButtonPress(WIIMOTE_BUTTON_RIGHT))
	{
		printf("TiVo Command: Right\n");
        tivoConnection->SendCommand("IRCODE RIGHT\r\n");
	}

	if (wm->ButtonPress(WIIMOTE_BUTTON_HOME))
	{
		printf("TiVo Command: Guide\n");
        tivoConnection->SendCommand("IRCODE GUIDE\r\n");
	}

	if (wm->ButtonPress(WIIMOTE_BUTTON_A))
	{
		printf("TiVo Command: Enter\n");
        tivoConnection->SendCommand("IRCODE ENTER\r\n");
	}

		
		/*
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_A))	printf("A pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_B))	printf("B pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_UP))	printf("UP pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_DOWN))	printf("DOWN pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_LEFT))	printf("LEFT pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_RIGHT))	printf("RIGHT pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_MINUS))	printf("MINUS pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_PLUS))	printf("PLUS pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_ONE))	printf("ONE pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_TWO))    printf("TWO pressed\n");
    if (IS_PRESSED(wm, WIIMOTE_BUTTON_HOME))	printf("HOME pressed\n");
	
    if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_MINUS))
        wiiuse_motion_sensing(wm, 0);

    if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_PLUS))
        wiiuse_motion_sensing(wm, 1);

    if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_UP))
        wiiuse_set_ir(wm, 1);
    if (IS_JUST_PRESSED(wm, WIIMOTE_BUTTON_DOWN))
        wiiuse_set_ir(wm, 0);


    

    if (WIIUSE_USING_IR(wm)) {
        int i = 0;

        for (; i < 4; ++i) {
            if (wm->ir.dot[i].visible)
                printf("IR source %i: (%u, %u)\n", i, wm->ir.dot[i].x, wm->ir.dot[i].y);
        }

        printf("IR cursor: (%u, %u)\n", wm->ir.x, wm->ir.y);
        printf("IR z distance: %f\n", wm->ir.z);
    }

    if (wm->exp.type == EXP_NUNCHUK) {
        struct nunchuk_t* nc = (nunchuk_t*)&wm->exp.nunchuk;

        if (IS_PRESSED(nc, NUNCHUK_BUTTON_C))		printf("Nunchuk: C pressed\n");
        if (IS_PRESSED(nc, NUNCHUK_BUTTON_Z))		printf("Nunchuk: Z pressed\n");

        printf("nunchuk roll  = %f\n", nc->orient.roll);
        printf("nunchuk pitch = %f\n", nc->orient.pitch);
        printf("nunchuk yaw   = %f\n", nc->orient.yaw);

        printf("nunchuk joystick angle:     %f\n", nc->js.ang);
        printf("nunchuk joystick magnitude: %f\n", nc->js.mag);
    } else if (wm->exp.type == EXP_CLASSIC) {
        struct classic_ctrl_t* cc = (classic_ctrl_t*)&wm->exp.classic;

        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZL))			printf("Classic: ZL pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_B))			printf("Classic: B pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_Y))			printf("Classic: Y pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_A))			printf("Classic: A pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_X))			printf("Classic: X pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_ZR))			printf("Classic: ZR pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_LEFT))		printf("Classic: LEFT pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_UP))			printf("Classic: UP pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_RIGHT))		printf("Classic: RIGHT pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_DOWN))		printf("Classic: DOWN pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_L))		printf("Classic: FULL L pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_MINUS))		printf("Classic: MINUS pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_HOME))		printf("Classic: HOME pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_PLUS))		printf("Classic: PLUS pressed\n");
        if (IS_PRESSED(cc, CLASSIC_CTRL_BUTTON_FULL_R))		printf("Classic: FULL R pressed\n");

        printf("classic L button pressed:         %f\n", cc->l_shoulder);
        printf("classic R button pressed:         %f\n", cc->r_shoulder);
        printf("classic left joystick angle:      %f\n", cc->ljs.ang);
        printf("classic left joystick magnitude:  %f\n", cc->ljs.mag);
        printf("classic right joystick angle:     %f\n", cc->rjs.ang);
        printf("classic right joystick magnitude: %f\n", cc->rjs.mag);
    }
	*/
}


/**
 *	@brief Callback that handles a read event.
 *
 *	@param wm		Pointer to a wiimote_t structure.
 *	@param data		Pointer to the filled data block.
 *	@param len		Length in bytes of the data block.
 *
 *	This function is called automatically by the wiiuse library when
 *	the wiimote has returned the full data requested by a previous
 *	call to wiiuse_read_data().
 *
 *	You can read data on the wiimote, such as Mii data, if
 *	you know the offset address and the length.
 *
 *	The \a data pointer was specified on the call to wiiuse_read_data().
 *	At the time of this function being called, it is not safe to deallocate
 *	this buffer.
 */
void handle_read(Wiimote* wm, byte* data, unsigned short len) {
    int i = 0;
	
    printf("\n\n--- DATA READ [wiimote id %i] ---\n", wm->GetID());
    printf("finished read of size %i\n", len);
    for (; i < len; ++i) {
        if (!(i%16))
            printf("\n");
        printf("%x ", data[i]);
    }
    printf("\n\n");
}


/**
 *	@brief Callback that handles a controller status event.
 *
 *	@param wm				Pointer to a wiimote_t structure.
 *	@param attachment		Is there an attachment? (1 for yes, 0 for no)
 *	@param speaker			Is the speaker enabled? (1 for yes, 0 for no)
 *	@param ir				Is the IR support enabled? (1 for yes, 0 for no)
 *	@param led				What LEDs are lit.
 *	@param battery_level	Battery level, between 0.0 (0%) and 1.0 (100%).
 *
 *	This occurs when either the controller status changed
 *	or the controller status was requested explicitly by
 *	wiiuse_status().
 *
 *	One reason the status can change is if the nunchuk was
 *	inserted or removed from the expansion port.
 */
void handle_ctrl_status(Wiimote* wm) {
	/*
    printf("\n\n--- CONTROLLER STATUS [wiimote id %i] ---\n", wm->unid);

    printf("attachment:      %i\n", wm->exp.type);
    printf("speaker:         %i\n", WIIUSE_USING_SPEAKER(wm));
    printf("ir:              %i\n", WIIUSE_USING_IR(wm));
    printf("leds:            %i %i %i %i\n", WIIUSE_IS_LED_SET(wm, 1), WIIUSE_IS_LED_SET(wm, 2), WIIUSE_IS_LED_SET(wm, 3), WIIUSE_IS_LED_SET(wm, 4));
    printf("battery:         %f %%\n", wm->battery_level);
	*/
}


/**
 *	@brief Callback that handles a disconnection event.
 *
 *	@param wm				Pointer to a wiimote_t structure.
 *
 *	This can happen if the POWER button is pressed, or
 *	if the connection is interrupted.
 */
void handle_disconnect(Wiimote* wm)
{
    printf("--- DISCONNECTED [wiimote id %i] ---\n", wm->GetID());
}

void ThreadFunc()
{
	while (Running)
	{
		wmManager->Update();

        for (int i = 0; i < MAX_WIIMOTES; ++i)
		{
            switch (wmManager->wiimotes[i]->GetEvent())
			{
                case WIIUSE_EVENT:
                    // A generic event occurred
                    handle_event(wmManager->wiimotes[i]);
                    break;

                case WIIUSE_STATUS:
                    // A status event occurred
                    handle_ctrl_status(wmManager->wiimotes[i]);
                    break;

                case WIIUSE_DISCONNECT:
                case WIIUSE_UNEXPECTED_DISCONNECT:
                    // The wiimote disconnected
                    handle_disconnect(wmManager->wiimotes[i]);
                    break;

                case WIIUSE_READ_DATA:
                    /*
                        *	Data we requested to read was returned.
                        *	Take a look at wiimotes[i]->read_req
                        *	for the data.
                        */
                    break;

                case WIIUSE_NUNCHUK_INSERTED:
                    //wiiuse_set_nunchuk_orient_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 90.0f);
                    //wiiuse_set_nunchuk_accel_threshold((struct nunchuk_t*)&wiimotes[i]->exp.nunchuk, 100);
                    printf("Nunchuk inserted.\n");
                    break;

                case WIIUSE_CLASSIC_CTRL_INSERTED:
                    printf("Classic controller inserted.\n");
                    break;

                case WIIUSE_NUNCHUK_REMOVED:
                case WIIUSE_CLASSIC_CTRL_REMOVED:
                    // Some expansion was removed
                    handle_ctrl_status(wmManager->wiimotes[i]);
                    printf("An expansion was removed.\n");
                    break;

                default:
                    break;
            }
        }
		boost::this_thread::sleep(boost::posix_time::milliseconds(10));
    }
}

int main(int argc, char** argv)
{
    last_channel_change = 0;
	Running = true;
	
	boost::shared_ptr<boost::asio::io_service> io_service(new boost::asio::io_service());

	wmManager = new WiimoteManager();
	wmManager->wiimotes[0]->EnableMotion();
	tivoConnection = new TiVoConnection(io_service);

	boost::thread updateThread(&ThreadFunc);
    



	char buffer;
	buffer = getchar();

	Running = false;
	updateThread.join();
	tivoConnection->Shutdown();
	wmManager->Shutdown();

	delete tivoConnection;
	delete wmManager;
}
